package cmd

import (
	"context"
	"fmt"
	"io"
	"strings"
	"time"

	"github.com/spf13/cobra"
	"go.bug.st/serial"
)

var (
	serialPort = "COM1"
	baudRate   = 115200
)

var burnCmd = &cobra.Command{
	Use:   "burn",
	Short: "烧录程序到设备",
	Long:  `通过串口检测USB设备并烧录程序`,
	RunE: func(cmd *cobra.Command, args []string) error {
		port, err := openSerialPort()
		if err != nil {
			return fmt.Errorf("打开串口失败: %v", err)
		}
		defer port.Close()
		// 切换到 root 权限
		if err := switchToRoot(port); err != nil {
			return fmt.Errorf("切换到 root 权限失败: %v", err)
		}

		// 1. 检测设备
		device, err := getFirstSDDevice(port)
		if err != nil {
			return fmt.Errorf("检测设备失败: %v", err)
		}

		// 2. 创建挂载点
		if err := createMountPoint(port); err != nil {
			return fmt.Errorf("创建挂载点失败: %v", err)
		}

		// 3. 处理已存在的挂载
		if err := handleExistingMount(port); err != nil {
			return fmt.Errorf("处理已存在的挂载失败: %v", err)
		}

		// 4. 挂载设备
		if err := mountDevice(port, device); err != nil {
			return fmt.Errorf("挂载设备失败: %v", err)
		}

		// 5. 复制文件
		if err := copyFiles(port); err != nil {
			return fmt.Errorf("复制文件失败: %v", err)
		}

		// 6. 安装和重启
		if err := installAndReboot(port); err != nil {
			return fmt.Errorf("安装和重启失败: %v", err)
		}

		return nil
	},
}

func openSerialPort() (serial.Port, error) {
	mode := &serial.Mode{
		BaudRate: baudRate,
	}
	port, err := serial.Open(serialPort, mode)
	if err != nil {
		return nil, fmt.Errorf("打开串口 %s 失败: %v", serialPort, err)
	}
	fmt.Printf("成功打开串口 %s\n", serialPort)
	return port, nil
}

func sendCommand(port serial.Port, command string) error {
	_, err := port.Write([]byte(command + "\n"))
	if err != nil {
		return fmt.Errorf("写入命令失败, 请检查连接是否断开: %v", err)
	}

	return nil
}

func switchToRoot(port serial.Port) error {
	fmt.Println("*** 开始切换到 root 用户 ***")
	// 检查当前用户
	err := sendCommand(port, "whoami")
	if err != nil {
		return fmt.Errorf("发送 whoami 命令失败: %v", err)
	}
	lines, err := readMultipleLines(port, 5*time.Second, 1*time.Second, 500*time.Millisecond)
	if err != nil {
		return fmt.Errorf("读取 whoami 命令响应失败: %v", err)
	}

	if len(lines) == 0 {
		return fmt.Errorf("whoami 命令没有返回响应")
	}

	for _, line := range lines {
		response := line
		if strings.TrimSpace(response) == "root" {
			fmt.Println("当前用户已经是 root, 无需切换")
			return nil
		}
	}

	// 发送 su 命令
	err = sendCommand(port, "su -")
	if err != nil {
		return fmt.Errorf("发送 su 命令失败: %v", err)
	}

	// 等待并检查响应，设置 10 秒超时
	fmt.Println("等待密码提示")
	lines, err = readMultipleLines(port, 5*time.Second, 1*time.Second, 500*time.Millisecond)
	if err != nil {
		return fmt.Errorf("等待密码提示失败: %v", err)
	}

	// 发送密码
	fmt.Println("发送密码")
	err = sendCommand(port, "orangepi")
	if err != nil {
		return fmt.Errorf("发送密码失败: %v", err)
	}

	// 等待并检查响应
	fmt.Println("等待 root 提示符")
	lines, err = readMultipleLines(port, 5*time.Second, 1*time.Second, 500*time.Millisecond)
	if err != nil {
		return fmt.Errorf("切换到 root 失败: %v", err)
	}

	fmt.Println("成功切换到 root 用户")

	return nil
}

func readMultipleLines(port serial.Port, timeout, portTimeout, maxIdleTime time.Duration) ([]string, error) {
	port.SetReadTimeout(portTimeout)

	ctx, cancel := context.WithTimeout(context.Background(), timeout)
	defer cancel()

	var lines []string
	buffer := make([]byte, 1)
	var line []byte
	lastReadTime := time.Now()

	for {
		select {
		case <-ctx.Done():
			// 总超时，返回已读取的行
			if len(line) > 0 {
				trimmedLine := strings.TrimSpace(string(line))
				if trimmedLine != "" {
					lines = append(lines, trimmedLine)
				}
			}
			return lines, nil
		default:
			n, err := port.Read(buffer)
			if err != nil {
				if err == io.EOF || time.Since(lastReadTime) > maxIdleTime {
					// EOF 或空闲超时，返回已读取的行
					if len(line) > 0 {
						trimmedLine := strings.TrimSpace(string(line))
						if trimmedLine != "" {
							lines = append(lines, trimmedLine)
						}
					}
					return lines, nil
				}
				// 其他错误，返回错误
				return lines, err
			}
			if n == 0 {
				continue
			}
			lastReadTime = time.Now()

			if buffer[0] == '\n' {
				trimmedLine := strings.TrimSpace(string(line))
				if trimmedLine != "" {
					lines = append(lines, trimmedLine)
				}
				line = []byte{}
			} else {
				line = append(line, buffer[0])
			}
		}
	}
}

func getFirstSDDevice(port serial.Port) (string, error) {
	fmt.Println("*** 开始检测 SD 设备 ***")

	err := sendCommand(port, "lsblk -ln")
	if err != nil {
		return "", fmt.Errorf("执行 lsblk 命令失败: %v", err)
	}

	lines, err := readMultipleLines(port, 3*time.Second, 1*time.Second, 500*time.Millisecond)
	if err != nil {
		return "", fmt.Errorf("读取 lsblk 命令输出失败: %v", err)
	}

	fmt.Printf("接收到 lsblk 命令输出:\n%s\n", strings.Join(lines, "\n"))

	for _, line := range lines {
		fields := strings.Fields(line)
		if len(fields) > 0 && strings.HasPrefix(fields[0], "sd") && strings.HasSuffix(fields[0], "1") {
			device := "/dev/" + fields[0]
			fmt.Printf("找到 SD 设备: %s\n", device)
			return device, nil
		}
	}
	return "", fmt.Errorf("没有找到合适的设备")
}

func createMountPoint(port serial.Port) error {
	fmt.Println("*** 开始创建挂载点 /mnt/usb ***")
	err := sendCommand(port, "sudo mkdir -p /mnt/usb")
	if err != nil {
		return fmt.Errorf("发送创建挂载点命令失败: %v", err)
	}

	lines, err := readMultipleLines(port, 3*time.Second, 1*time.Second, 500*time.Millisecond)
	if err != nil {
		return fmt.Errorf("读取创建挂载点命令输出失败: %v", err)
	}

	if len(lines) > 0 {
		fmt.Printf("创建挂载点命令输出:\n%s\n", strings.Join(lines, "\n"))
	} else {
		fmt.Println("创建挂载点命令没有输出")
	}

	// 检查是否有错误信息在输出中
	for _, line := range lines {
		if strings.Contains(strings.ToLower(line), "error") || strings.Contains(strings.ToLower(line), "failed") {
			return fmt.Errorf("创建挂载点可能失败: %s", line)
		}
	}

	fmt.Println("创建挂载点成功")
	return nil
}

func handleExistingMount(port serial.Port) error {
	fmt.Println("*** 检查是否存在已挂载的 /mnt/usb ***")
	err := sendCommand(port, "mount")
	if err != nil {
		return fmt.Errorf("发送 mount 命令失败: %v", err)
	}

	lines, err := readMultipleLines(port, 3*time.Second, 1*time.Second, 500*time.Millisecond)
	if err != nil {
		return fmt.Errorf("读取 mount 命令输出失败: %v", err)
	}

	mountOutput := strings.Join(lines, "\n")
	if strings.Contains(mountOutput, "/mnt/usb") {
		fmt.Println("检测到 /mnt/usb 已挂载，尝试卸载")
		err := sendCommand(port, "sudo umount /mnt/usb")
		if err != nil {
			return fmt.Errorf("发送卸载 /mnt/usb 命令失败: %v", err)
		}

		unmountLines, err := readMultipleLines(port, 3*time.Second, 1*time.Second, 500*time.Millisecond)
		if err != nil {
			return fmt.Errorf("读取卸载命令输出失败: %v", err)
		}

		unmountOutput := strings.Join(unmountLines, "\n")
		fmt.Printf("卸载 /mnt/usb 输出:\n%s\n", unmountOutput)

		// 检查卸载是否成功
		if strings.Contains(strings.ToLower(unmountOutput), "error") || strings.Contains(strings.ToLower(unmountOutput), "failed") {
			return fmt.Errorf("卸载 /mnt/usb 失败: %s", unmountOutput)
		}
		fmt.Println("成功卸载 /mnt/usb")
	} else {
		fmt.Println("/mnt/usb 未挂载，无需卸载")
	}
	return nil
}

func mountDevice(port serial.Port, device string) error {
	fmt.Printf("*** 尝试挂载设备 %s 到 /mnt/usb ***\n", device)
	err := sendCommand(port, fmt.Sprintf("sudo mount %s /mnt/usb", device))
	if err != nil {
		return fmt.Errorf("发送挂载命令失败: %v", err)
	}

	lines, err := readMultipleLines(port, 3*time.Second, 1*time.Second, 500*time.Millisecond)
	if err != nil {
		return fmt.Errorf("读取挂载命令输出失败: %v", err)
	}

	mountOutput := strings.Join(lines, "\n")
	fmt.Printf("挂载设备输出:\n%s\n", mountOutput)

	// 检查挂载是否成功
	if strings.Contains(strings.ToLower(mountOutput), "error") || strings.Contains(strings.ToLower(mountOutput), "failed") {
		return fmt.Errorf("挂载设备失败: %s", mountOutput)
	}
	fmt.Println("成功挂载设备")
	return nil
}

func copyFiles(port serial.Port) error {
	fmt.Println("*** 开始复制文件 ***")

	// 复制程序文件
	fmt.Println("复制 orangepi-cli-arm 文件")
	err := sendCommand(port, "cp /mnt/usb/orangepi-cli-arm .")
	if err != nil {
		return fmt.Errorf("发送复制 orangepi-cli-arm 命令失败: %v", err)
	}

	lines, err := readMultipleLines(port, 3*time.Second, 1*time.Second, 500*time.Millisecond)
	if err != nil {
		return fmt.Errorf("读取复制 orangepi-cli-arm 命令输出失败: %v", err)
	}

	copyOutput := strings.Join(lines, "\n")
	fmt.Printf("复制 orangepi-cli-arm 输出:\n%s\n", copyOutput)

	// 复制配置文件
	fmt.Println("复制 config.json 文件")
	err = sendCommand(port, "sudo cp /mnt/usb/config.json /etc/robotic_config.json")
	if err != nil {
		return fmt.Errorf("发送复制 config.json 命令失败: %v", err)
	}

	lines, err = readMultipleLines(port, 3*time.Second, 1*time.Second, 500*time.Millisecond)
	if err != nil {
		return fmt.Errorf("读取复制 config.json 命令输出失败: %v", err)
	}

	copyConfigOutput := strings.Join(lines, "\n")
	fmt.Printf("复制 config.json 输出:\n%s\n", copyConfigOutput)

	return nil
}

func installAndReboot(port serial.Port) error {
	fmt.Println("*** 开始安装命令和服务 ***")

	// 安装命令
	fmt.Println("安装命令")
	err := sendCommand(port, "sudo ./orangepi-cli-arm install command")
	if err != nil {
		return fmt.Errorf("发送安装命令失败: %v", err)
	}

	lines, err := readMultipleLines(port, 3*time.Second, 1*time.Second, 500*time.Millisecond)
	if err != nil {
		return fmt.Errorf("读取安装命令输出失败: %v", err)
	}

	installCommandOutput := strings.Join(lines, "\n")
	fmt.Printf("安装命令输出:\n%s\n", installCommandOutput)

	// 安装服务
	fmt.Println("安装服务")
	err = sendCommand(port, "sudo ./orangepi-cli-arm install service")
	if err != nil {
		return fmt.Errorf("发送安装服务命令失败: %v", err)
	}

	lines, err = readMultipleLines(port, 3*time.Second, 1*time.Second, 500*time.Millisecond)
	if err != nil {
		return fmt.Errorf("读取安装服务输出失败: %v", err)
	}

	installServiceOutput := strings.Join(lines, "\n")
	fmt.Printf("安装服务输出:\n%s\n", installServiceOutput)

	// 重启系统
	fmt.Println("*** 准备重启系统 ***")
	err = sendCommand(port, "sudo reboot")
	if err != nil {
		return fmt.Errorf("发送重启命令失败: %v", err)
	}

	lines, err = readMultipleLines(port, 3*time.Second, 1*time.Second, 500*time.Millisecond)
	if err != nil {
		return fmt.Errorf("读取重启命令输出失败: %v", err)
	}

	rebootOutput := strings.Join(lines, "\n")
	fmt.Printf("重启命令输出:\n%s\n", rebootOutput)

	// 添加重复不断读取行的功能，不使用现有函数，不设置任何超时
	continuousRead(port)

	return nil
}

func continuousRead(port serial.Port) error {
	fmt.Println("开始持续读取串口数据...")
	buffer := make([]byte, 1)
	var line []byte

	for {
		n, err := port.Read(buffer)
		if err != nil {
			if err == io.EOF {
				// EOF 通常意味着连接已关闭，但我们可能希望继续尝试读取
				fmt.Println("检测到 EOF，等待新数据...")
				time.Sleep(100 * time.Millisecond)
				continue
			}
			return fmt.Errorf("读取串口数据时发生错误: %v", err)
		}

		if n == 0 {
			continue
		}

		if buffer[0] == '\n' {
			if len(line) > 0 {
				fmt.Printf("%s\n", string(line))
				line = []byte{}
			}
		} else {
			line = append(line, buffer[0])
		}
	}
}

func init() {
	burnCmd.Flags().StringVar(&serialPort, "port", "COM1", "串口号")
	burnCmd.Flags().IntVar(&baudRate, "baud", 115200, "波特率")

	rootCmd.AddCommand(burnCmd)
}
